<script lang="ts">
  import { ImageInfo, uploadImage, UploadImageResponse } from '@/api/upload';
  import { Message } from '@/libs';
  import { guid } from '@/utils/math';
  import { getImageInfo } from '@/utils/uniPromise';
  import { computed, defineComponent, PropType, ref } from '@vue/composition-api';

  export type PropsType = {
    uploadMoreStyle: {
      src: string;
    };
    max: number;
    count: number;
    autoUpload: boolean;
  };

  export type ImageItem = {
    id: string;
    saveStatus: 'local' | 'cloud';
    uploadStatus: 'success' | 'failure' | 'uploading' | 'not-upload';
    path: string;
    cloudPath?: string;
  } & Partial<ImageInfo>;

  export type UploadError = string | null;
  export type UploadSucces = UploadImageResponse | null;
  export type Result = [UploadError, UploadSucces];
  export type EmitEventKey = 'input' | 'getImages';

  export default defineComponent({
    props: {
      uploadMoreStyle: {
        type: Object as PropType<PropsType['uploadMoreStyle']>,
        default: () => {
          return { src: '' } as PropsType['uploadMoreStyle'];
        }
      },
      max: {
        type: Number as PropType<PropsType['max']>,
        default: 9
      },
      count: {
        type: Number as PropType<PropsType['count']>,
        default: 9
      },
      autoUpload: {
        type: Boolean as PropType<PropsType['autoUpload']>,
        default: true
      }
    },
    emits: ['input', 'getImages'] as EmitEventKey[],
    setup(props, { emit }) {
      const { max, count, autoUpload } = props;
      const list = ref<ImageItem[]>([]);
      const isMax = computed(() => {
        return list.value.length === max;
      });

      function useEmit(event: EmitEventKey, ...args: any[]) {
        emit(event, ...args);
      }

      function uploadEmit(data: Record<EmitEventKey, unknown>) {
        Object.keys(data).forEach((key) => {
          useEmit(key as EmitEventKey, data[key as EmitEventKey]);
        });
      }

      type UploadCallback = (index: number, data: Result) => void;

      async function fileListItemUpload(file: ImageItem) {
        const res = await getImageInfo(file.path);
        const { width, height } = res;
        Object.assign(file, { width, height });
        return uploadImage(file.path, {
          width,
          height
        });
      }

      function uploadList(fileList: ImageItem[], cb: UploadCallback): Promise<Result[]> {
        const uploadRequestList = fileList.map((file) => {
          return fileListItemUpload(file);
        });
        const len = uploadRequestList.length;
        let count = 0;
        const resultList: Result[] = [];
        return new Promise((resolve) => {
          uploadRequestList.forEach((p, index) => {
            Promise.resolve(p)
              .then((result) => {
                console.log('成功上传图片', result);
                count++;
                resultList[index] = [null, result.data];
                cb(index, [null, result.data]);
                if (count === len) {
                  resolve(resultList);
                }
              })
              .catch((err) => {
                console.error('上传图片失败', err);
                count++;
                resultList[index] = [JSON.stringify(err), null];
                cb(index, [JSON.stringify(err), null]);
                if (count === len) {
                  resolve(resultList);
                }
              });
          });
        });
      }

      function uploadSuccess(file: ImageItem, cloudPath: string) {
        let findIndex = -1;
        let findItem: ImageItem | undefined;
        list.value.forEach((item, index) => {
          if (item.id === file.id) {
            findIndex = index;
            findItem = item;
          }
        });
        if (findIndex !== -1 && findItem) {
          findItem.saveStatus = 'cloud';
          findItem.uploadStatus = 'success';
          findItem.cloudPath = cloudPath;
          list.value[findIndex] = findItem;
        }

        uploadEmit({
          input: list.value,
          getImages: list.value
        });
      }

      function uploadFailure(file: ImageItem) {
        let findIndex = -1;
        let findItem: ImageItem | undefined;
        list.value.forEach((item, index) => {
          if (item.id === file.id) {
            findIndex = index;
            findItem = item;
          }
        });
        if (findIndex !== -1 && findItem) {
          findItem.saveStatus = 'local';
          findItem.uploadStatus = 'failure';
          list.value[findIndex] = findItem;
        }
      }

      async function uploadFile(fileList: ImageItem[]) {
        uploadList(fileList, (index: number, [err, result]) => {
          // 图片上传失败则
          if (err) {
            uploadFailure(fileList[index]);
          } else {
            // 图片上传成功则
            uploadSuccess(fileList[index], result?.url || '');
          }
        });
      }

      function selectFile() {
        uni.chooseImage({
          count: count,
          sourceType: ['album', 'camera'],
          sizeType: 'compressed',
          success: (chooseImageRes) => {
            let tempFilePaths: ImageItem[] = [];
            if (typeof chooseImageRes.tempFilePaths === 'string') {
              tempFilePaths = [
                {
                  id: guid(),
                  saveStatus: 'local',
                  uploadStatus: autoUpload ? 'uploading' : 'not-upload',
                  path: chooseImageRes.tempFilePaths
                }
              ];
            } else {
              tempFilePaths = chooseImageRes.tempFilePaths.map((path: string) => {
                const imageItem: ImageItem = {
                  id: guid(),
                  saveStatus: 'local',
                  uploadStatus: autoUpload ? 'uploading' : 'not-upload',
                  path: path
                };
                return imageItem;
              });
            }

            if (list.value.concat(tempFilePaths).length > max) {
              return Message.warning(`您还能选择${max - list.value.length}张！`);
            }

            tempFilePaths.forEach((item) => {
              list.value.push(item);
            });

            if (autoUpload) {
              uploadFile(tempFilePaths);
            } else if (max === 1) {
              uploadEmit({
                input: list.value[0].path,
                getImages: list.value
              });
            } else {
              uploadEmit({
                input: list.value,
                getImages: list.value
              });
            }
          }
        });
      }
      async function retry(item: ImageItem, index: number) {
        // const { item, index } = e.currentTarget.dataset;
        console.log(item, index);
        fileListItemUpload(item)
          .then((res) => {
            uploadSuccess(item, res.data.url || '');
          })
          .catch((err) => {
            console.log(err);
            uploadFailure(item);
          });
      }

      return {
        selectFile,
        retry,
        list,
        isMax
      };
    }
  });
</script>

<template>
  <view class="upload">
    <view class="upload-list">
      <!-- 已上传的图片列表 -->
      <template v-for="(item, index) in list">
        <view class="upload-list-item upload-item-common" :key="index">
          <image class="image" :data-src="item.path" :src="item.path" mode="aspectFill" @click.stop="preview" />
          <image
            class="delete"
            src="/static/components/oto-image-upload/close.png"
            :data-item="item"
            @click.stop="deleteImg"
          />
          <view class="success" v-if="item.uploadStatus === 'success'">
            <view class="success-body">
              <view class="success-content"></view>
              <image class="success-icon" src="/static/components/oto-image-upload/upload-success.png" mode="" />
            </view>
          </view>
          <view class="uploading" v-if="item.uploadStatus === 'uploading'">上传中</view>
          <view
            class="retry"
            :data-item="item"
            :data-index="index"
            @click.stop="retry(item, index)"
            v-if="item.uploadStatus === 'failure'"
            >点击重试</view
          >
        </view>
      </template>
      <!-- 上传更多按钮 -->
      <view class="upload-more upload-item-common" @click.stop="selectFile" v-if="!isMax">
        <view class="upload-more-style" v-if="!uploadMoreStyle.src">
          <view class="transverse"></view>
          <view class="vertical"></view>
        </view>
        <image v-if="uploadMoreStyle.src" :src="uploadMoreStyle.src" mode="" />
      </view>
    </view>
  </view>
</template>

<style lang="scss" scoped>
  .upload {
    .upload-list {
      margin-left: -12rpx;
      .upload-item-common {
        display: inline-block;
        width: 176rpx;
        height: 176rpx;
        padding: 0 0 12rpx 12rpx;
      }

      .upload-list-item {
        position: relative;

        .uploading {
          position: absolute;
          left: 12rpx;
          right: 0;
          bottom: 12rpx;
          color: #fff;
          background-color: forestgreen;
          font-size: 20rpx;
          padding: 8rpx 0;
          text-align: center;
          position: absolute;
        }

        .retry {
          position: absolute;
          left: 12rpx;
          right: 0;
          bottom: 12rpx;
          color: #fff;
          background-color: #fa3534;
          font-size: 20rpx;
          padding: 8rpx 0;
          text-align: center;
          position: absolute;
        }
        .image {
          width: 100%;
          height: 100%;
        }
        .delete {
          position: absolute;
          width: 32rpx;
          height: 32rpx;
          top: -12rpx;
          right: -10rpx;
        }
        .success {
          position: absolute;
          top: 0rpx;
          left: 12rpx;
          .success-body {
            position: relative;
            .success-content {
              width: 0;
              height: 0;
              border-top: 50rpx solid forestgreen;
              border-right: 50rpx solid transparent;
            }
            .success-icon {
              width: 32rpx;
              height: 32rpx;
              position: absolute;
              top: 0;
            }
          }
        }
      }
    }

    .upload-more {
      opacity: 1;

      .upload-more-style {
        display: inline-block;
        background: #ededed;
        position: relative;
        width: 100%;
        height: 100%;

        // 横
        .transverse {
          position: absolute;
          left: 50%;
          top: 50%;
          transform: translate(-50%, -50%);
          width: 109rpx;
          height: 5rpx;
          border-radius: 8rpx;
          opacity: 1;
          background: rgba(218.88, 218.88, 218.88, 1);
        }

        // 竖
        .vertical {
          position: absolute;
          left: 50%;
          top: 50%;
          transform: translate(-50%, -50%);
          width: 5rpx;
          height: 109rpx;
          border-radius: 8rpx;
          opacity: 1;
          background: rgba(218.88, 218.88, 218.88, 1);
        }
      }
    }
  }
</style>
